/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package deatax.game.model.controller;

import deatax.game.controller.Controller;
import deatax.game.model.ModelMaterial;
import deatax.game.model.ModelMesh;
import deatax.game.model.ModelNode;
import deatax.game.model.ModelSegment;
import deatax.math.Matrix4;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import javax.media.opengl.GL;
import javax.media.opengl.GL2;
import javax.media.opengl.GLAutoDrawable;

/**
 *
 * @author pson
 */
public class ModelController implements Controller {

    protected ModelNode node;
    protected boolean useVBO;

    public ModelController(ModelNode node) {
        this.node = node;
    }

    public int getVBuffer(GL2 gl, ModelMesh mesh) {
        int vb = mesh.vbuffer;
        if (vb <= 0) {
            int[] t = new int[1];
            gl.glGenBuffers(1, t, 0);
            vb = t[0];
            mesh.vbuffer = vb;

            gl.glBindBuffer(GL.GL_ARRAY_BUFFER, vb);
            //4 = sizeof float
            gl.glBufferData(GL.GL_ARRAY_BUFFER, mesh.verts.length * 4, FloatBuffer.wrap(mesh.verts), GL.GL_STATIC_DRAW);

            
            gl.glBindBuffer(GL.GL_ARRAY_BUFFER, vb);
            //xyz
            gl.glEnableVertexAttribArray(0);
            gl.glVertexAttribPointer(0, 3, GL.GL_FLOAT, false, 8 * 4, 0);
            //nnn
            gl.glEnableVertexAttribArray(1);
            gl.glVertexAttribPointer(1, 3, GL.GL_FLOAT, false, 8 * 4, 12);
            //uv
            gl.glEnableVertexAttribArray(2);
            gl.glVertexAttribPointer(2, 2, GL.GL_FLOAT, false, 8 * 4, 24);
        }
        //System.out.println("VB is " + vb);
        return vb;
    }

    public int getFBuffer(GL2 gl, ModelMesh mesh) {
        int fb = mesh.fbuffer;
        if (fb <= 0) {
            int[] t = new int[1];
            gl.glGenBuffers(1, t, 0);
            fb = t[0];
            mesh.fbuffer = fb;

            gl.glBindBuffer(gl.GL_ELEMENT_ARRAY_BUFFER, fb);
            //4 = sizeof int
            gl.glBufferData(gl.GL_ELEMENT_ARRAY_BUFFER, mesh.faces.length * 4, IntBuffer.wrap(mesh.faces), gl.GL_STATIC_DRAW);
        }
        //System.out.println("FB is " + fb);
        return fb;
    }

    protected void applyMaterial(GL2 gl, ModelMaterial mat) {
        if (mat.texture > 0) {
            //gl.glBindTexture(GL.GL_TEXTURE_2D, );
            //TEXTURE!
        }
        gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL2.GL_DIFFUSE, mat.diffuse, 0);
        gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL2.GL_SPECULAR, mat.specular, 0);
        gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL2.GL_AMBIENT, mat.ambient, 0);
        gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL2.GL_EMISSION, mat.emissive, 0);
        gl.glMaterialf(GL.GL_FRONT_AND_BACK, GL2.GL_SHININESS, mat.shininess);
    }

    protected void drawSegment(GL2 gl, ModelSegment seg) {
        gl.glPushMatrix();

        if (seg.transform != null) {
            Matrix4 m = seg.transform.transposed();
            gl.glMultMatrixf(FloatBuffer.wrap(m.v));
        }
        for (int i = 0; i < seg.mesh.length; ++i) {
            ModelMesh mesh = node.meshes[seg.mesh[i]];
            applyMaterial(gl, node.materials[mesh.mat]);

            if (useVBO) {
                gl.glBindBuffer(GL.GL_ARRAY_BUFFER, getVBuffer(gl, mesh));
                gl.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, getFBuffer(gl, mesh));

                gl.glDrawRangeElements(GL.GL_TRIANGLES, 0, mesh.verts.length / 8, mesh.faces.length, GL.GL_UNSIGNED_INT, 0);
            } else {
                //crap draw

                int[] face = mesh.faces;
                float[] vert = mesh.verts;
                for (int j = 0; j < face.length; j += 3) {

                    int v0 = face[j] * 8;
                    int v1 = face[j + 1] * 8;
                    int v2 = face[j + 2] * 8;

                    gl.glBegin(GL.GL_TRIANGLES);

                    gl.glTexCoord2f(vert[v0 + 6], vert[v0 + 7]);
                    gl.glNormal3f(vert[v0 + 3], vert[v0 + 4], vert[v0 + 5]);
                    gl.glVertex3f(vert[v0], vert[v0 + 1], vert[v0 + 2]);

                    gl.glTexCoord2f(vert[v1 + 6], vert[v1 + 7]);
                    gl.glNormal3f(vert[v1 + 3], vert[v1 + 4], vert[v1 + 5]);
                    gl.glVertex3f(vert[v1], vert[v1 + 1], vert[v1 + 2]);

                    gl.glTexCoord2f(vert[v2 + 6], vert[v2 + 7]);
                    gl.glNormal3f(vert[v2 + 3], vert[v2 + 4], vert[v2 + 5]);
                    gl.glVertex3f(vert[v2], vert[v2 + 1], vert[v2 + 2]);


                    gl.glEnd();
                }
            }
        }
        for (ModelSegment ms : seg.children) {
            drawSegment(gl, ms);
        }
        gl.glPopMatrix();
    }

    @Override
    public void init(GL2 gl) {

        if (!gl.isFunctionAvailable("glGenBuffers")
                || !gl.isFunctionAvailable("glBindBuffer")
                || !gl.isFunctionAvailable("glBufferData")
                || !gl.isFunctionAvailable("glDeleteBuffers")) {
            //System.out.println("NO VBO FOR YOU");
            useVBO = false;
        } else {
            //System.out.println("VBO FOR JUSTICE!");
            useVBO = true;
        }
    }

    @Override
    public void update(long time) {
    }

    @Override
    public void draw(GL2 gl, long time) {
        if (node != null) {
            drawSegment(gl, node.root);
        }
    }
}
